#include <iostream>

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <cstring>
#include <unistd.h>
#include <cerrno>

#include "Thread.hpp"
#include "InetAddr.hpp"

using namespace std;

void Usage(const string &process)
{
    cout << "Usage: " << process << " server_ip server_ip" << endl;
}

class ThreadDate
{
public:
    ThreadDate(int sock,struct sockaddr_in &addr):_sockfd(sock),_addr(addr)
    {}
    ~ThreadDate()
    {}

    int _sockfd;
    InetAddr _addr;
};

void RecvRoutine(ThreadDate &td)
{
    char buffer[1024];
    // 收消息
    while(true)
    {
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);

        ssize_t m = recvfrom(td._sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &len); // 虽然没用但也是要填的temp
        //m放回读到的字节个数
        if (m > 0)
        {
            buffer[m] = 0;
            cerr << buffer << endl;
        }
        else
            break;
    }


}

void SendRoutine(ThreadDate &td)
{
 while (true)
    {
        string inbuffer;
        cout << "Please Enter# ";
        getline(cin, inbuffer);
        auto server = td._addr.GetAddr();
        // 发给：
        ssize_t n = sendto(td._sockfd, inbuffer.c_str(), inbuffer.size(), 0, (struct sockaddr *)&(server), sizeof(server));
        if(n <= 0) cerr << "send error" << endl;
    }
}

//./udp_client server_ip server_port  向服务器发消息
int main(int argc, char **argv)
{
    if (argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }

    string serverip = argv[1]; // 字符串的点分十进制
    uint16_t serverport = stoi(argv[2]);

    // 1. 创建套接字,创建文件信息
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        cerr << "socket error: " << strerror(errno) << endl;
        return 2;
    }
    cout << "create socket success: " << sock << endl;
    // 2. 绑定
    // 其中客户端的socket也要绑定，但是不需要显示bind（client会在首次发送数据的时候会自动进行bind）
    // 不推荐自己绑定，为什么？服务器的端口号一定是众所周知的，不可改变。而client 需要 port，让其随机bind端口
    // client一般不绑定一个确定的socket因为：client 会非常多（打开如何软件），可能会导致别人占了你的端口
    // 就导致无法启动

    // 2.1 填充server信息：
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());

    // 创建一个线程执行受消息
    ThreadDate td(sock,server);
    Thread<ThreadDate> recver("recv", RecvRoutine, td);
    Thread<ThreadDate> sender("recv", SendRoutine, td);
    recver.Start();
    sender.Start();

 
    recver.Join();
    sender.Join();


    close(sock); // 把文件描述符关掉
    return 0;
}